#pragma once

#include <base.h>

#include <task/sched.h>

#define PROCESSOR_ONLINE BIT(0)
#define PROCESSOR_ENABLED BIT(1)
#define PROCESSOR_BSP BIT(63)

#define GDT_KERNEL_CS_IDX (1)
#define GDT_USER_CS_IDX (2)
#define GDT_USER_DS_IDX (3)
#define GDT_TSS_IDX (4)

#define GDT_MAKE_SELECTOR(idx, rpl) (((idx) << 3) | (rpl & 3))

#define MAKE_GDT_CS64_ENTRY(dpl) (BIT(53) | BIT(47) | (((uint64_t)dpl & 3) << 45) | BIT(44) | BIT(43))
#define MAKE_GDT_DS64_ENTRY(dpl) (BIT(47) | (((uint64_t)dpl & 3) << 45) | BIT(44) | BIT(41))

typedef struct __packed {
	uint32_t reserved0;
	uintptr_t rsp[3];
	uint64_t reserved1;
	uintptr_t ist[7];
	uint64_t reserved2;
	uint16_t reserved3;
	uint16_t iopb;
} tss_t;

typedef struct __packed {
	uint32_t apic_id;
	uint32_t acpi_id;

	uint64_t gdt[6];
	tss_t tss;

	uint64_t attribute;
	uint64_t sched_tick_counter;

	task_block_t *current_task;
	task_block_t *idle_task;
} processor_t;

void processor_register(uint32_t apic_id, uint32_t acpi_id, uint32_t flags);
processor_t *processor_get(uint32_t apic_id);
processor_t *processor_get_current();

void processor_foreach(void (*visitor)(processor_t *));

void processor_self_init();